package com.edu.juc.lock.lockUpdate;

import org.openjdk.jol.info.ClassLayout;

/*
 * 锁升级
 * 锁升级过程：
 *  无锁->偏向锁->轻量级锁->重量级锁
 *
 * synchronized锁：由对象头Mark Word根据锁标志位的不同而被复用及锁升级策略。
 *
 * Java5以前，只有Synchronized，这个是操作系统级别的重量级操作。
 *
 * Java的线程是映射到操作系统原生线程之上的，如果要阻塞或唤醒一个线程就需要操作系统介入，需要在用户态和内核态之间切换，
 * 这种切换会消耗大量的系统资源。
 * 为了减少获得锁和释放锁所带来的性能消耗，引入了轻量级锁和偏向锁。
 *
 * Monitor与Java对象以及线程是如何关联的？
 * 1.如果一个Java对象被某个线程锁柱，则该Java对象的Mark Word字段中LockWord指向monitor的起始地址。
 * 2.Monitor的Owner字段会存放拥有相关联对象锁的线程ID。
 * Mutex Lock的切换需要从用户态切换到核心态中，因此状态转换需要消耗很多的处理器时间。
 *
 * 偏向锁：MarkWord存储的是偏向的线程ID；
 * 轻量级锁：MarkWord存储的是指向线程栈中Lock Record的指针；
 * 重量级锁：MarkWord存储的是指向堆中的Monitor对象的指针。
 *
 * 当一个线程执行到synchronized代码块时，JVM使用CAS操作把线程指针ID记录到MarkWord中，并修改偏向标识，标识当前线程获得锁。
 * 锁对象变成偏向锁（通过CAS修改对象头中的锁标识位），“偏向于第一个获得它的线程”的锁。
 * 执行完同步代码块后，线程并不会主动释放偏向锁。
 *
 * 在重量级锁的实现中，对象头指向了重量级锁的位置，代表重量级锁的ObjectMonitor类里面有字段可以记录非加锁状态(01)下的Mark Word，其中自然可以存储原来的哈希码。
 */
public class Main {
    public static void main(String[] args) throws InterruptedException {
        // 1. 无锁：初始状态，一个对象被实例化后，如果还没有被任何线程竞争锁，那么它就为无锁状态。
        // 001
        System.out.println("========无锁=========");
        Object object = new Object();
        System.out.println(ClassLayout.parseInstance(object).toPrintable());
        System.out.println(Integer.toBinaryString(object.hashCode()));
        System.out.println(ClassLayout.parseInstance(object).toPrintable());
        // 2. 偏向锁：单线程竞争
        // 101
        // 当线程A第一次竞争到锁时，通过操作修改MarkWord中的偏向线程ID、偏向模式。
        // 如果不存在其它线程竞争，那么持有偏向锁的线程将永远不需要进行同步。
        // 当一段同步代码一直被同一个线程访问多次，由于只有一个线程，那么该线程在后续访问时便会自动获得锁。
        // 不需要再次加锁和释放锁。不涉及用户到内核态转换，不必要直接升级为最高级。
        // JDK 版本对偏向锁的变更
        // JDK 15 起默认关闭偏向锁
        // 从 JDK 15 开始，偏向锁默认被禁用（-XX:-UseBiasedLocking）。即使显式启用参数 -XX:+UseBiasedLocking，实际运行中也可能无法触发偏向锁状态
        // JDK 17 彻底移除偏向锁
        //  在 JDK 17 中，偏向锁的相关代码已被完全移除。对象初始化后直接进入轻量级锁状态（标记位 00），不再支持偏向锁的分配和撤销流程
        System.out.println("========偏向锁=========");

        synchronized (object) {
            // 偏向锁在JDK1.6以上默认开启，开启后程序启动4秒后才会被激活，可以使用JVM参数来关闭延迟
            // 激活：-XX:BiasedLockingStartupDelay=0
            System.out.println(ClassLayout.parseInstance(object).toPrintable());
            System.out.println(Integer.toBinaryString(object.hashCode()));
        }
        // 当有另外线程逐步来竞争锁的时候，就不能再使用偏向锁了，要升级为轻量级锁
        // 竞争线程尝试CAS 修改MarkWord中的线程ID，会等待到全局安全点(此时不会执行任何代码)撤销偏向锁。

        // 3. 轻量级锁：多线程竞争
        // 00
        // JVM会为每个线程在当前线程的栈帧中创建用于存储锁记录的空间，官方称为Displaced Mark Word。
        // 若一个线程获得锁时发现是轻量级锁，会把锁的MarkWord复制到自己的Displaced Mark Word中，
        // 并尝试用CAS把锁的MarkWord修改为指向锁记录的指针。
        // 如果成功，当前线程获得锁，如果失败，表示MarkWord已经被替换成了其他线程的锁记录，当前线程就尝试使用自旋（一定次数仍失败后，升级为重量级锁）来获取锁。
        System.out.println("========轻量级锁=========");
        // 轻量级锁的释放：
        // 在释放锁时，当前线程会使用CAS操作将Displaced Mark Word的内容复制回锁的Mark Word里。
        // 如果没有发生竞争，那么这个复制操作会成功。
        // 如果有其他线程因为自旋多次导致轻量级锁升级成了重量级锁，那么CAS会失败，此时会释放锁并唤醒被阻塞的线程。

        // 4. 重量级锁
        // 10
        // Java中synchronized的重量级锁，是基于进入和退出Monitor对象实现的。
        // 当线程执行monitorenter指令时，会尝试获取对象所对应的Monitor所有权，
        // 如果获取到了，即获取到了锁，会在Monitor的owner中存放当前线程的ID，这样它将处于锁定状态，除非退出同步块，否则其他线程无法获取到这个monitor。

    }

}
